package yun.common.http;

import java.net.SocketTimeoutException;
import java.net.URI;
import java.util.List;
import javax.net.ssl.HttpsURLConnection;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import yun.common.http.request.Request;
import yun.common.http.response.ErrorResponse;
import yun.common.http.response.IResponse;
import yun.common.http.response.IResponseFactory;

public abstract class RequestHelper {
	private Object mLock = new Object();
	private OnResponseListener mOnResponseListener;
	private Request mRequest;
	private HttpClientThread mThread;
	private IResponseFactory mResponseFactory;
	private HttpRequestBase mHttpRequest;
	private boolean mCanceled;

	// Callback
	public interface OnResponseListener {
		void onStart(Request request);
		void onCancel(Request request);
		void onResponse(Request request, IResponse response);
	}

	public RequestHelper(OnResponseListener l, IResponseFactory factory) {
		mOnResponseListener = l;
		mResponseFactory = factory;
	}

	public void sendRequest(Request request) {
		sendRequest(request, HttpPost.METHOD_NAME);
	}

	public void sendRequest(Request request, String method) {
		abort();
		//
		mCanceled = false;
		mRequest = request;
		mResponseFactory = request.getResponseFactory();
		invokeOnStart();
		// run
		mThread = new HttpClientThread(method);
		mThread.start();
	}

	private void invokeOnStart() {
		if (mOnResponseListener != null && mRequest != null) {
			mOnResponseListener.onStart(mRequest);
		}
	}

	private void invokeOnCancel() {
		if (mOnResponseListener != null && mRequest != null) {
			mOnResponseListener.onCancel(mRequest);
			mRequest = null;
		}
	}

	private void invokeOnResponse(IResponse response) {
		if (mOnResponseListener != null && !mCanceled && mRequest != null && response != null) {
			mOnResponseListener.onResponse(mRequest, response);
		}
	}

	public void cancel() {
		synchronized (mLock) {
			mCanceled = true;
			invokeOnCancel();
			abort();
		}
	}

	private void abort() {
		synchronized (mLock) {
			if (mHttpRequest != null) {
				mHttpRequest.abort();
				mHttpRequest = null;
			}
		}
	}

	protected abstract boolean isOnline();
	protected abstract void saveCookie(URI uri, List<Cookie> cookies);
	protected abstract List<Cookie> getCookies(URI uri);

	//
	class HttpClientThread extends Thread {
		private String mMethod;
		public HttpClientThread(String method) {
			mMethod = method;
		}

		@Override
		public void run() {
			IResponse response = execute();
			synchronized (mLock) {
				if (!mCanceled) {
					invokeOnResponse(response);
				}
			}
		}

		private IResponse execute() {
			// INIT
			DefaultHttpClient httpClient = new DefaultHttpClient();
			HttpParams httpParams = httpClient.getParams();
			HttpConnectionParams.setSoTimeout(httpParams, 1000 * 10);
			HttpConnectionParams.setConnectionTimeout(httpParams, 1000 * 30);

			try {

				// cancel check
				synchronized (mLock) {
					if (mCanceled) {
						return null;
					}

					if (HttpPost.METHOD_NAME.equalsIgnoreCase(mMethod)) {
						mHttpRequest = mRequest.getHttpPost();
					} else {
						mHttpRequest = mRequest.getHttpGet();
					}
				}

				// online check
				if (!isOnline()) {
					return new ErrorResponse(ErrorResponse.OFF_LINE);
				}

				// URI
				URI uri = mHttpRequest.getURI();

				// fill cookies
				List<Cookie> cookies = getCookies(uri);
				if (!cookies.isEmpty()) {
					CookieStore cookieStore = new BasicCookieStore();
					for (Cookie cookie : cookies) {
						cookieStore.addCookie(cookie);
					}
					httpClient.setCookieStore(cookieStore);
				}

				// send
				HttpResponse response = httpClient.execute(mHttpRequest);

				// save cookies
				List<Cookie> cookie = httpClient.getCookieStore().getCookies();
				if (!cookie.isEmpty()) {
					saveCookie(uri, cookie);
				}

				// HTTP OK?
				StatusLine statusLine = response.getStatusLine();
				int statusCode = statusLine.getStatusCode();
				if (statusCode != HttpsURLConnection.HTTP_OK) {
					return new ErrorResponse(ErrorResponse.HTTP_NG, statusLine.toString());
				}

				// create response
				return mResponseFactory.newResponse(mRequest, response);

			} catch (SocketTimeoutException e) {
				e.printStackTrace();
				return new ErrorResponse(ErrorResponse.SOCKET_TIME_OUT);
			} catch (ConnectTimeoutException e) {
				e.printStackTrace();
				return new ErrorResponse(ErrorResponse.CONNECT_TIME_OUT);
			} catch (Exception e) {
				e.printStackTrace();
				return new ErrorResponse(ErrorResponse.UNKNOWN, e);
			} finally {
				httpClient.getConnectionManager().shutdown();
			}
		}
	}
}
